package com.sky.service.impl;

import com.sky.entity.Orders;
import com.sky.entity.Sale;
import com.sky.mapper.OrderDetailMapper;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.ReportService;
import com.sky.service.WorkService;
import com.sky.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ReportServiceImpl implements ReportService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private WorkService workService;

    /**
     * 统计指定时间区间内的营业额
     *
     * @param begin
     * @param end
     * @return
     */
    public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {

        List<LocalDate> dateList = getLocalDateList(begin, end);
        //将数组中的数据按,分割开转化成字符串
        String dataString = StringUtils.join(dateList, ",");

        //定义一个数组用于存放从数据库读取到的日期统计金额 营业额是已经完成的订单金额合计
        List<Double> turnoverList = new ArrayList<>();
        //对上面存储日期的数据进行遍历
        dateList.forEach(date -> {
            //创建一个map进行对数据存储
            Map map = new HashMap();
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
            map.put("begin", beginTime);
            map.put("end", endTime);
            map.put("status", Orders.COMPLETED);
            // select sum(amount) from orders where createTime>=begin and endTime <=end and status =Orders.COMPLETED
            Double turnover = orderMapper.sumAmountByMap(map);
            turnover = turnover == null ? 0.0 : turnover;
            //将金额再添加进存放营业额的数组中
            turnoverList.add(turnover);
        });
        //将装有金额的数组转换成字符串并用,进行分割开来
        String turnoverString = StringUtils.join(turnoverList, ",");
        TurnoverReportVO turnoverReportVO = TurnoverReportVO.builder()
                .dateList(dataString)
                .turnoverList(turnoverString)
                .build();
        return turnoverReportVO;
    }

    /**
     * 抽取一个共同的方法
     * 将区间内的时间拆分放入数组中
     *
     * @param begin
     * @param end
     * @return
     */
    private List<LocalDate> getLocalDateList(LocalDate begin, LocalDate end) {
        //当前用于存放begin到end范围内的每天的日期
        List<LocalDate> dateList = new ArrayList<>();
        dateList.add(begin);
        while (!begin.equals(end)) {
            //日期计算,计算指定日期的最后一天对应的日期
            begin = begin.plusDays(1);
            dateList.add(begin);
        }
        return dateList;
    }

    /**
     * 订单统计接口
     *
     * @param begin
     * @param end
     * @return
     */
    public OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end) {
        //获取一个装有起始时间区间的数组
        List<LocalDate> dateList = getLocalDateList(begin, end);
        String dateListStr = StringUtils.join(dateList, ",");//将数组用,分割转换成字符串
        //创建一个存放日期区间订单总数的结集合
        List<Integer> orderSumList = new ArrayList<>();
        //创建一个存放日期区间有效订单数的集合
        List<Integer> orderCompletedList = new ArrayList<>();
        dateList.forEach(date -> {
            //将存放日期段的数组细分开始和结束时间
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
            //创建一个map对数据进行存储
            Map map = new HashMap();
            map.put("begin", beginTime);
            map.put("end", endTime);
            //从数据库中读取时间段内所有的订单 订单完成率=已完成的订单/订单总数
            //订单数列表: 每个时间段内总的订单数
            Integer orderSum = orderMapper.sumIdByMap(map);//获取区间时间段总的订单数
            //对返回值为null的数据进行赋值0的操作
            orderSum = orderSum == null ? 0 : orderSum;
            orderSumList.add(orderSum);
            map.put("status", Orders.COMPLETED);
            Integer orderCompletedSum = orderMapper.sumIdByMap(map);//获取区间时间段有效的订单数
            orderCompletedSum = orderCompletedSum == null ? 0 : orderCompletedSum;
            orderCompletedList.add(orderCompletedSum);
        });
        //计算订单完成率 1。统计总的订单数 2.统计有效的订单数
        int sum = getSum(orderSumList);//总订单数
        int completed = getSum(orderCompletedList);//有效订单数
        double completionRate;//订单完成率
        if (sum == 0) {
            completionRate = 0.0;
        } else {
            completionRate = (double) completed / sum;//订单完成率
        }

        //封装返回给前端
        String orderSumListStr = StringUtils.join(orderSumList, ",");//订单总数列表
        String orderCompletedListStr = StringUtils.join(orderCompletedList, ",");//有效订单数列表
        //创建对象封装
        OrderReportVO orderReportVO = OrderReportVO.builder()
                .dateList(dateListStr)
                .orderCountList(orderSumListStr)
                .orderCompletionRate(completionRate)
                .totalOrderCount(sum)
                .validOrderCount(completed)
                .validOrderCountList(orderCompletedListStr)
                .build();

        return orderReportVO;
    }

    /**
     * 用户统计接口
     *
     * @param begin
     * @param end
     * @return
     */
    public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
        //获取一个装有时间区间段的集合
        List<LocalDate> dateList = getLocalDateList(begin, end);
        //创建一个存储区间新增用户数量的集合进行存储
        List<Integer> userSumList = new ArrayList<>();
        //区间段查询新增用户列表
        dateList.forEach(date -> {
            //获取单个时间段的区间从当天凌晨到午夜的时间端区间
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
            Map map = new HashMap();
            map.put("begin", beginTime);
            map.put("end", endTime);
            //调用mapper进行查询
            Integer sumUser = userMapper.sumUserIdByMap(map);//查询出区间段的用户新增数量
            //存入集合中 这里先要做一个判断 当查出为null的时候我们要给他转成0
            sumUser = sumUser == null ? 0 : sumUser;
            userSumList.add(sumUser);
        });
        //分别将存储的时间段的列表集合和新增用户集合封装给前端
        String userListStr = StringUtils.join(userSumList, ",");
        String dateListStr = StringUtils.join(dateList, ",");
        //创建返回 对象
        UserReportVO userReportVO = UserReportVO.builder()
                .dateList(dateListStr)
                .newUserList(userListStr)
                .totalUserList(userListStr)
                .build();
        return userReportVO;
    }

    /**
     * 查询区间内销量排名top10接口
     *
     * @param begin
     * @param end
     * @return
     */
    public SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end) {
        //创建一个接收时间区间内订单销量名字的集合
        List<String> nameList = new ArrayList<>();
        //创建一个接收时间区间内订单量的集合
        List<Integer> numberList = new ArrayList<>();
        LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);
        Map map = new HashMap();
        map.put("begin", beginTime);
        map.put("end", endTime);
        List<Sale> salesTop10 = orderDetailMapper.getSalesTop10(map);
        salesTop10.forEach(sale -> {
            //分别取出数据库封装返回对象的名字和数量
            String productName = sale.getProductName();
            int sales = sale.getSales();
            nameList.add(productName);
            numberList.add(sales);
        });
        //此时已经将数据全部装入了对应的集合中
        //将集合转换成字符串
        String nameListStr = StringUtils.join(nameList, ",");
        String numberListStr = StringUtils.join(numberList, ",");
        //创建前端返回对象封装响应
        SalesTop10ReportVO salesTop10ReportVO = SalesTop10ReportVO.builder()
                .nameList(nameListStr)
                .numberList(numberListStr)
                .build();
        return salesTop10ReportVO;
    }

    /**
     * 导出运营数据报表
     *
     * @param response
     */
    public void exportBusinessDate(HttpServletResponse response) {
        //获取今天日期
        LocalDate now = LocalDate.now();
        LocalDate begin = now.minusDays(30);//往前推30天
        LocalDate end = now.minusDays(1);//昨天
        //获取当天时间最小值
        LocalDateTime min = LocalDateTime.of(begin, LocalTime.MIN);
        LocalDateTime max = LocalDateTime.of(end, LocalTime.MAX);
        //从数据库查询信息
        BusinessDataVO businessData2Time = workService.getBusinessData2Time(min, max);
        //通过poi将数据填充到我们的模板里面去
        //获得一个输入流对象
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");
        try {
            XSSFWorkbook excel = new XSSFWorkbook(inputStream);
            //进行数据填充
            //概览数据填充
            XSSFSheet sheet1 = excel.getSheet("Sheet1");
            //填充时间
            sheet1.getRow(1).getCell(1).setCellValue(begin + "至" + end);
            //填充营业额
            sheet1.getRow(3).getCell(2).setCellValue(businessData2Time.getTurnover());
            //填充订单完成率
            sheet1.getRow(3).getCell(4).setCellValue(businessData2Time.getOrderCompletionRate());
            //填充新增用户数
            sheet1.getRow(3).getCell(6).setCellValue(businessData2Time.getNewUsers());
            //填充有效订单
            sheet1.getRow(4).getCell(2).setCellValue(businessData2Time.getValidOrderCount());
            //填充平均客单价
            sheet1.getRow(4).getCell(4).setCellValue(businessData2Time.getUnitPrice());
            //填充明细数据 实现思路:先获取每一天的时间进行查询 再根据表单单元格填充数量进行遍历写入
            //先将存有日期集合的日期逐个取出去求得每天的运营数据
            int count = 0;//用户计数
            for (int i = 7; i <= 36; i++) {
                LocalDate date = begin.plusDays(count);//将天数往后推
                //将取到的日期逐个进行拆分成一天
                //当前时间
                BusinessDataVO businessData2Time1 = workService.getBusinessData2Time(LocalDateTime.of(date, LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));
//                Integer newUsers = businessData2Time1.getNewUsers();
//                Integer validOrderCount = businessData2Time1.getValidOrderCount();
//                Double turnover = businessData2Time1.getTurnover();
//                Double unitPrice = businessData2Time1.getUnitPrice();
//                Double orderCompletionRate = businessData2Time1.getOrderCompletionRate();
                //获取行对象
                XSSFRow row = sheet1.getRow(i);//获取i行对象
                for (int j = 1; j <= 6; j++) {
                    XSSFCell cell = row.getCell(j);//获取列单元格
                    //进行写入逐条数据
                    cell.setCellValue(date.toString());
                    cell.setCellValue(businessData2Time1.getTurnover());
                    cell.setCellValue(businessData2Time1.getValidOrderCount());
                    cell.setCellValue(businessData2Time1.getOrderCompletionRate());
                    cell.setCellValue(businessData2Time1.getUnitPrice());
                    cell.setCellValue(businessData2Time1.getNewUsers());
                }
                count++;
            }
            //此时数据已经填写完毕 返回给前端
            ServletOutputStream outputStream = response.getOutputStream();
            excel.write(outputStream);
            //释放资源
            outputStream.close();
            excel.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //返回给前端
    }

    /**
     * 写一个统计数值类型集合的方法
     */
    private int getSum(List<Integer> list) {
        int sum = 0;
        for (Integer integer : list) {
            sum += integer;
        }
        return sum;
    }
}
